﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace SerialManager
{
    /// <summary>
    /// Class for pattern matching of season and episode number in file names using regular expressions
    /// </summary>
    public class EpisodeFilePattern : Regex
    {
        /// <summary>
        /// Creates built-in patterns
        /// Order of creation defines priority in matching process
        /// </summary>
        static EpisodeFilePattern()
        {
            // 01x02 1x2 01-02
            AddPattern("(?<s>[0-9]{1,2})[-x](?<e>[0-9]{1,2})");
            // s01e02 S1E2 s01E2
            AddPattern("[sS](?<s>[0-9]{1,2})[eE](?<e>[0-9]{1,2})");
            // 0102 102 (means season 1, episode 2), eliminates 720p and x264 matches
            AddPattern("(?!720p)(?!x264)(?<s>[0-9]{1,2})(?<e>[0-9]{2})");
        }

        private static log4net.ILog Logger =
            log4net.LogManager.GetLogger(typeof(EpisodeFilePattern));

        private static List<EpisodeFilePattern> _patterns =
            new List<EpisodeFilePattern>();

        /// <summary>
        /// Looks for pattern matches in <see cref="FileInfo.Name"/> of given file
        /// </summary>
        /// <param name="file">File to check</param>
        /// <returns>List of possible matches sorted by pattern priority</returns>
        public static EpisodePatternMatch[] SearchForMatch(FileInfo file)
        {
            return (
                from p in _patterns
                let match = p.Match(file.Name)
                where match.Success && match.Length > 0
                select new EpisodePatternMatch(p, match)
            ).ToArray();
        }

        /// <summary>
        /// Creates new pattern from string
        /// </summary>
        /// <param name="pattern">Regular expression, must contain named groups 's' and 'e' with matched numbers</param>
        protected static void AddPattern(string pattern)
        {
            AddPattern(new EpisodeFilePattern(pattern));
        }

        /// <summary>
        /// Add new pattern to list
        /// </summary>
        /// <param name="pattern">Created instance of pattern</param>
        protected static void AddPattern(EpisodeFilePattern pattern)
        {
            _patterns.Add(pattern);
            Console.WriteLine(pattern.GetHashCode());
        }

        /// <summary>
        /// Construct new pattern and checks if contains required named groups
        /// <see cref="RegexOptions"/> are set to <see cref="RegexOptions.Compiled"/> and <see cref="RegexOptions.Singleline"/>
        /// </summary>
        /// <param name="pattern">Regular expression, must contain named groups 's' and 'e' with matched numbers</param>
        protected EpisodeFilePattern(string pattern) : 
            base(pattern, RegexOptions.Compiled | RegexOptions.Singleline)
        {
            if (pattern.IndexOf("?<s>") == -1)
                Logger.FatalFormat("Pattern doesn't contain '?<s>' sequence for indicating season number");
            else if (pattern.IndexOf("?<e>") == -1)
                Logger.FatalFormat("Pattern doesn't contain '?<e>' sequence for indicating episode number");
            else
                return;
            
            Logger.Debug(pattern);
        }

        /// <summary>
        /// Gets unique hash code based on pattern
        /// </summary>
        public override int GetHashCode()
        {
            return this.pattern.GetHashCode();
        }
    }

    /// <summary>
    /// Wraps information about one match by EpisodeFilePattern
    /// </summary>
    public class EpisodePatternMatch
    {
        /// <summary>
        /// Number of matched season
        /// </summary>
        public int Season { get; private set; }
        /// <summary>
        /// Number of matched episode
        /// </summary>
        public int Episode { get; private set; }
        /// <summary>
        /// Pattern that was used to create this match
        /// Used when deciding which match in list should be used
        /// </summary>
        public EpisodeFilePattern Pattern { get; private set; }
        /// <summary>
        /// Converts values from match to numeric properties
        /// </summary>
        /// <param name="match">Successful match</param>
        public EpisodePatternMatch(EpisodeFilePattern pattern, Match match)
        {
            Season = Convert.ToInt32(match.Groups["s"].Value);
            Episode = Convert.ToInt32(match.Groups["e"].Value);
            Pattern = pattern;
        }

        /// <summary>
        /// Unique hash code for this match
        /// </summary>
        public override int GetHashCode()
        {
            return GetHashCode(Season, Episode);
        }

        /// <summary>
        /// Compares two instances of <see cref="EpisodeFilePattern"/>
        /// Can also be compared to <see cref="EpisodeInfo"/>
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Creates unique identification for season and episode pair
        /// </summary>
        /// <param name="season">Number of season</param>
        /// <param name="episode">Number of episode</param>
        /// <returns>Calculated hash code</returns>
        public static int GetHashCode(int season, int episode)
        {
            return (season * 1000) + episode;
        }
    }
}
